#include "newprot.h"

#include <errno.h>

static fstream* m_logfile;

void init_newprot(fstream* logfile) {
	m_logfile=logfile;
}

void ReportSystemError(string err, bool quit)
{
	string fullerr=string("Error:") + err;
	perror(fullerr.data());
	
	if(m_logfile != NULL)
		(*m_logfile) << fullerr << strerror(errno) << endl;
	
	if (quit) exit(FAILURE);
}

string getIPfromhost(const char * name)
{
    struct hostent * he;
    struct in_addr **addr_list;
    char ipstr[INET_ADDRSTRLEN];
    char* ip = ipstr;

    he = gethostbyname(name);
    if (he==NULL)
    {
    	herror("gethostbyname ");
    	exit(1);

    }
    addr_list = (struct in_addr **)he->h_addr_list;

    ip =  inet_ntoa(*addr_list[0]);

    return string(ip);
}


/******
 *	returns -1 on error
 */
int sendMsg(int fd, string msg)
{
    int bytesSent = 0; //sent so far
    int len = msg.length();
    int res = 0;

    while(bytesSent < len)
    {
            res = send(fd, msg.data()+bytesSent , len - bytesSent,0); //sending setting the flag to 0;
            if (res == -1)
            {
            	return FAILURE;
            }
            bytesSent += res; ///updating the number of bytes sent
    }

    return SUCCESS;
}

/*****
 * Receives a message, exits on error. 0 on success,
 * -1 on user disconnection or error
 */
int recvMsg(int fd, string& msg, int timeout_seconds)
{
	int rec = 0;
    //int bytesRead = 0;
    char buf[LEN+1];
    memset(buf, '\0',LEN+1);
    //while ((rec = recv(fd,buf + bytesRead ,LEN - bytesRead,0)))
    //while ((rec = read(fd,buf + bytesRead ,LEN - bytesRead)))
	fd_set socks;
	struct timeval timeout;
	timeout.tv_sec=timeout_seconds;
	timeout.tv_usec=0;
	
	struct timeval* timeoutp=NULL;
	if(timeout_seconds!=0)
		timeoutp=&timeout;
	
	msg.clear();
    do {
		FD_ZERO(&socks);
		FD_SET(fd,&socks);
		int readable=select(fd+1, &socks, NULL, NULL, timeoutp);
		if(readable==0 || (timeout.tv_sec==0 && timeout.tv_usec==0)) return FAILURE;
		
		rec = read(fd, buf, LEN);
		
		if (rec == -1)
		{
			ReportSystemError("read ", true);
			return FAILURE;
		}
		
		buf[rec]='\0';
		msg+=buf;
		//bytesRead += rec;

    } while (rec!=0 && memchr(buf,'\n',LEN)==NULL);

	if(msg=="") return FAILURE;
    return SUCCESS;
}

/***
 * Returns the socket
 */
int connect(int port, string address)
{
	//address = "127.0.0.1";

	string ip = getIPfromhost(address.data());
	struct sockaddr_in addr;
    addr.sin_family = AF_INET; //setting address family
    addr.sin_port = htons(port); //setting server port
    inet_aton(ip.data(), &(addr.sin_addr)); //setting server address
    memset(&(addr.sin_zero), '\0',sizeof(addr.sin_zero));

    int st = socket(PF_INET,SOCK_STREAM,0); //creating socket for stream
    if (st == -1)
    {
            ReportSystemError("socket ",true);
    }

    if (connect(st,(sockaddr*)&addr,sizeof(struct sockaddr)))
    {
    	ReportSystemError("connect ", true);
    }
    return st;
}

/*
 * Returns the socket
 */
int listen(int port)
{
    //setting socket address for our own ip
    struct sockaddr_in addr;
    addr.sin_family = AF_INET; //setting address family
    addr.sin_port = htons(port); //setting remote port
    addr.sin_addr.s_addr = htonl(INADDR_ANY); //setting our own address
    memset(&(addr.sin_zero), '\0',sizeof(addr.sin_zero)); //setting to 0

	int st = socket(PF_INET,SOCK_STREAM,0); //creating socket for stream
    if (st == -1)
    {
    	ReportSystemError("socket ", true);
    }

	int yes=1;
	if (setsockopt (st, SOL_SOCKET, SO_REUSEADDR, &yes,sizeof(int)) == -1)
	{
		ReportSystemError("setsockopt ", true);
	}

    if (bind(st,(struct sockaddr*)&addr,sizeof(addr))==FAILURE) // associate the socket with the port
    {
    	ReportSystemError("bind ",true);
    }

    listen(st,BACKLOG);
    return st;
}

/***
 * Accepts the connection on given socket, returns new socket
 */
int accept(int fd)
{
    //for the new connection
    struct sockaddr_in newAddr; //the client address
    socklen_t addrlen = sizeof(struct sockaddr);
	int newSocket = accept(fd,(sockaddr*)&newAddr,&addrlen);
	if (newSocket == -1)
		ReportSystemError("accept ", true);
	return newSocket;
}
